package com.tang.lts.core.nio.processor;

import com.tang.lts.core.nio.NioClient;
import com.tang.lts.core.nio.channel.ChannelInitializer;
import com.tang.lts.core.nio.channel.NioChannel;
import com.tang.lts.core.nio.channel.NioChannelImpl;
import com.tang.lts.core.nio.config.NioClientConfig;
import com.tang.lts.core.nio.handler.Futures;
import com.tang.lts.core.nio.handler.NioHandler;
import com.tang.lts.core.nio.loop.NioSelectorLoop;

import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;

/**
 * @author Tang
 * @classname NioClientProcessor
 * @description [  ]
 * @date 2022/5/16 15:07
 */
public class NioClientProcessor extends AbstractNioProcessor {

    private NioClientConfig clientConfig;
    private NioChannelImpl nioChannel;

    public NioClientProcessor(NioClientConfig clientConfig,NioHandler eventHandler, ChannelInitializer channelInitialize) {
        super(eventHandler, channelInitialize);
        this.clientConfig = clientConfig;
    }

    @Override
    public NioChannel doAccept(NioSelectorLoop selectorLoop) {
        throw new UnsupportedOperationException();
    }

    @Override
    protected NioChannel doConnect(SocketAddress remoteAddress, NioSelectorLoop selectorLoop, Futures.ConnectFuture connectFuture) {
        SocketChannel channel = newSocket();
        try {
            if (clientConfig.getConnectTimeout() > 0) {
                channel.socket().setSoTimeout(clientConfig.getConnectTimeout());
            }
            if (clientConfig.getTcpNoDelay() != null) {
                channel.socket().setTcpNoDelay(clientConfig.getTcpNoDelay());
            }
            if (clientConfig.getReceiveBufferSize() != null) {
                channel.socket().setReceiveBufferSize(clientConfig.getReceiveBufferSize());
            }
            if (clientConfig.getKeepAlive() != null) {
                channel.socket().setKeepAlive(clientConfig.getKeepAlive());
            }
            if (clientConfig.getReuseAddress() != null) {
                channel.socket().setReuseAddress(clientConfig.getReuseAddress());
            }
            if (clientConfig.getIpTos() != null) {
                channel.socket().setTrafficClass(clientConfig.getIpTos());
            }
            if (clientConfig.getOobInline() != null) {
                channel.socket().setOOBInline(clientConfig.getOobInline());
            }
            if (clientConfig.getSoLinger() != null) {
                channel.socket().setSoLinger(true, clientConfig.getSoLinger());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        this.nioChannel = new NioChannelImpl(selectorLoop, this, channel, eventHandler(), clientConfig);
        try {
            channel.connect(remoteAddress);
            channel.register(this.selectorLoop.selector(), SelectionKey.OP_CONNECT);
        } catch (IOException e) {
            connectFuture.setChannel(nioChannel);
            connectFuture.setCause(e);
            connectFuture.setSuccess(false);
            return null;
        }
        channelInitializer.initChannel(nioChannel);
        this.idleDetector.addChannel(nioChannel);
        connectFuture.setChannel(nioChannel);
        connectFuture.setSuccess(true);
        return nioChannel;
    }

    private SocketChannel newSocket() {
        SocketChannel channel = null;
        try {
            channel = SocketChannel.open();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return channel;
    }

    @Override
    public void connect(SelectionKey key) {
        try {
            this.nioChannel.socketChannel().finishConnect();
            key.attach(this.nioChannel);
        } catch (IOException e) {
            e.printStackTrace();
        }
        key.interestOps(SelectionKey.OP_READ);
    }
}